import re
from datetime import datetime
from fastapi import FastAPI,HTTPException
from fastapi.responses import Response
from fastapi.routing import APIRoute
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.types import ASGIApp, Receive, Scope, Send
from app.core.dependency import AuthControl, AuthCustomerControl
from app.models.admin import AuditLog, User
from app.models.mini import MiniAuditLog
from redis import asyncio as aioredis
from .bgtask import BgTasks
import jwt,json
from app.settings import settings


class SimpleBaseMiddleware:
    def __init__(self, app: ASGIApp) -> None:
        self.app = app

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        request = Request(scope, receive=receive)
        response = await self.before_request(request) or self.app
        await response(request.scope, request.receive, send)
        await self.after_request(request)

    async def before_request(self, request: Request):
        return self.app

    async def after_request(self, request: Request):
        return None


class BackGroundTaskMiddleware(SimpleBaseMiddleware):
    async def before_request(self, request):
        await BgTasks.init_bg_tasks_obj()

    async def after_request(self, request):
        await BgTasks.execute_tasks()


class HttpAuditLogMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, methods: list, exclude_paths: list):
        super().__init__(app)
        self.methods = methods
        self.exclude_paths = exclude_paths
        self.redis_instance = None

    async def get_request_log(self, request: Request, response: Response) -> dict:
        """
        根据request和response对象获取对应的日志记录数据
        """
        data: dict = {"path": request.url.path, "status": response.status_code, "method": request.method}
        # 路由信息
        app: FastAPI = request.app
        for route in app.routes:
            if (
                    isinstance(route, APIRoute)
                    and route.path_regex.match(request.url.path)
                    and request.method in route.methods
            ):
                data["module"] = ",".join(route.tags)
                data["summary"] = route.summary
        # 获取用户信息
        token = request.headers.get("token")
        user_obj = None
        if token:
            user_obj: User = await AuthControl.is_authed(token)
        data["user_id"] = user_obj.id if user_obj else 0
        data["username"] = user_obj.username if user_obj else ""

        try:
            json_data = json.loads(request._body)  # 从缓存中解析
        except ValueError as e:
            json_data = None

        data["datas"] = json_data
        return data


    async def mini_log(self, request: Request, response: Response) -> dict:
        data: dict = {"path": request.url.path, "status": response.status_code, "method": request.method}
        # 路由信息
        app: FastAPI = request.app
        for route in app.routes:
            if (
                    isinstance(route, APIRoute)
                    and route.path_regex.match(request.url.path)
                    and request.method in route.methods
            ):
                data["module"] = ",".join(route.tags)
                data["summary"] = route.summary
        # 获取用户信息
        token = request.headers.get("token")
        user_id = 0
        user_name = ""
        if token:
            if token=="dev":
                user_id = 0
                user_name = "admin"
            else:
                try:
                    decode_data = jwt.decode(token, settings.SECRET_KEY, algorithms=settings.JWT_ALGORITHM)
                    user_id = decode_data.get("user_id")
                except jwt.DecodeError:
                    user_id = 0
                except jwt.ExpiredSignatureError:
                    user_id = 0
                except Exception as e:
                    user_id = 0
        data["user_id"] = user_id
        try:
            json_data = json.loads(request._body)  # 从缓存中解析
        except ValueError as e:
            json_data = None
        data["username"] = user_name
        data["datas"] = json_data
        return data

    async def before_request(self, request, call_next):
        if self.redis_instance is None:
            self.redis_instance = aioredis.from_url(settings.REDIS)
        request.state.redis = self.redis_instance

    async def after_request(self, request: Request, response: Response, process_time: int):
        if request.method in self.methods:  # 请求方法为配置的记录方法
            # 设备类型,空为后端,mini为小程序
            device_type = request.headers.get("devicetype")
            if device_type == "mini":
                ##这边加个日志
                data: dict = await self.mini_log(request=request, response=response)
            else:
                for path in self.exclude_paths:
                    if re.search(path, request.url.path, re.I) is not None:
                        return
                data: dict = await self.get_request_log(request=request, response=response)#这地方可能需要try一下
            if data["method"] == "GET":
                return
            data["response_time"] = process_time  # 响应时间
            if device_type == "mini":

                await MiniAuditLog.create(**data)
            else:
                await AuditLog.create(**data)

    async def dispatch(self, request: Request, call_next) -> Response:
        start_time: datetime = datetime.now()
        # 读取请求体并缓存
        body = await request.body()
        request._body = body  # 缓存请求体
        await self.before_request(request,call_next)
        response = await call_next(request)
        end_time: datetime = datetime.now()
        process_time = int((end_time.timestamp() - start_time.timestamp()) * 1000)
        await self.after_request(request, response, process_time)
        return response




